﻿using DotNetCommon;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;

namespace DBUtil.Provider.Sqlite;

#region sqlite 表名规则总结
//参照: https://blog.csdn.net/u010476739/article/details/152324976
// 总结: sqlite 同时支持 ""、[]、`` 引用符, 推荐 ""
// 当使用 "" 的解析规则:
//     1. 双引号内部 \t \r\n 没有转义的效果, 就是普通的字符, 甚至直接回车换行也不影响
//     2. 两个双引号代表一个普通字符 '"'
//     3. 示例:
//          create table "test"(id int,name varchar(50)) -- test

//          create table "te
//          ok
//          st"(id int,name varchar(50)) -- te换行符ok换行符st

//          create table "te\tst"(id int,name varchar(50)) -- te\tst
//          create table "te\r\nst"(id int,name varchar(50)) -- te\r\nst
//
// 当使用 `` 的解析规则: 和 "" 没有发现什么不同
//
// 当使用 [] 的解析规则: 整体上和 "" 一致, 但如果表名内部有 ']' 则不行(sqlserver是可以解析的)
//      直接看示例:
//          create table [test](id int,name varchar(50)) -- test

//          create table [te
//          ok
//          st](id int,name varchar(50)) -- te换行符ok换行符st
//          
//          create table [te\tst](id int,name varchar(50)) -- te\tst
//          create table [te\r\nst](id int,name varchar(50)) -- te\r\nst
//          create table [[[te[st](id int,name varchar(50)) -- [[te[st//
//          create table [te]]st](id int,name varchar(50)) -- error
#endregion

#region sqlite 中的 schema
//sqlite 没有像 mysq、sqlsever 一样的schema管理, 因为本来就是单文件, 但可以将其他的db文件附加过来,作为schema, 如:
// ```sql
// -- 现在自己的db里创建一张表
// create table main_test(id int,name varchar(50))
// -- 将其他的db文件附加进来
// attach database 'E:\Resources\新建文件夹\testsqlite2.db' AS otherdb; -- 注意: 同一个db文件可以以两个不同别名附件进来两次(移除使用: detach database otherdb2)
// -- 在附加进来的db文件里创建一张表
// create table otherdb.t_user(id int)
// pragma table_list; -- 查询表
// ```
// schema       name                 type       ncol   wr     strict
// main         main_test            table      2      0      0
// main         sqlite_schema        table      5      0      0
// temp         sqlite_temp_schema   table      5      0      0
// otherdb      t_user               table      1      0      0
// otherdb      sqlite_schema        table      5      0      0
#endregion

public partial class SqliteAccess : DBAccess
{
    #region 表名或标识符分析/解析
    private static readonly IReadOnlyList<string> _QuotedCharacters = ["\"\"", "``", "[]"];
    /// <inheritdoc />
    public override IReadOnlyList<string> QuotedCharacters => _QuotedCharacters;
    /// <inheritdoc />
    public override string AddQuote(string pureName)
    {
        if (pureName.IsNullOrEmpty()) return pureName;
        return $"\"{pureName.Replace("\"", "\"\"")}\"";
    }
    /// <inheritdoc />
    public override IEnumerable<string> AnalysisQuotedName(string quotedName)
    {
        var len = quotedName?.Length ?? 0;
        if (len == 0) yield break;

        var currentIdx = 0;

        void readWhiteSpaceOrDot()
        {
            while (currentIdx < len && (char.IsWhiteSpace(quotedName[currentIdx]) || quotedName[currentIdx] == '.')) currentIdx++;
        }
        string readQuotedSeg(char endChar, bool combineDouble)
        {
            var startIdx = currentIdx;
            var end = false;
            List<int> skipIdxes = null;

            while (currentIdx < len)
            {
                if (quotedName[currentIdx] != endChar)
                {
                    //没找到结束符号
                    currentIdx++;
                    continue;
                }

                //找到了结束符号
                if (!combineDouble)
                {
                    //不需要合并, 直接返回
                    end = true;
                    break;
                }

                //需要再判断一步: 两个双引号合并成一个
                currentIdx++;
                if (currentIdx >= len || quotedName[currentIdx] != endChar)
                {
                    //不一样 不能合并
                    currentIdx--;
                    end = true;
                    break;
                }
                //两个双引号, 忽略后面的一个
                skipIdxes ??= new List<int>(8);
                skipIdxes.Add(currentIdx);
                currentIdx++;
            }

            if (!end) throw new Exception($"对象名格式错误(结尾处缺少符号: 【{endChar}】): {quotedName}");
            if (skipIdxes == null)
            {
                //没有合并 简单情况如: "testdb"."table"
                return quotedName[startIdx..currentIdx];
            }
            else
            {
                //有合并情况 如: "tes\"\"tdb"."table"
                var tmpList = new List<char>(64);
                for (int i = startIdx; i < currentIdx; i++)
                {
                    if (skipIdxes.Contains(currentIdx)) continue;
                    tmpList.Add(quotedName[i]);
                }
                return new string([.. tmpList]);
            }
        }

        string readUnQuotedSeg()
        {
            var startIdx = currentIdx;
            while (currentIdx < len)
            {
                if (quotedName[currentIdx] == '.' || char.IsWhiteSpace(quotedName[currentIdx]))
                {
                    return quotedName[startIdx..currentIdx];
                }
                currentIdx++;
            }
            return quotedName[startIdx..];
        }

        //消耗左侧空白及dot(.)
        readWhiteSpaceOrDot();
        if (currentIdx >= len) yield break;

        //读取第一次
        var curChar = quotedName[currentIdx];
        if (curChar == '"' || curChar == '`' || curChar == '[')
        {
            //有引用符
            currentIdx++;
            yield return readQuotedSeg(curChar == '[' ? ']' : curChar, curChar != '[');
            currentIdx++;//读取停止的时候指针一定对着最后一个 ` 或 " 所以要跳一个
        }
        else
        {
            //没有引用符
            yield return readUnQuotedSeg();
            //读取停止的时候指针一定对着 . 或空白符
        }

        readWhiteSpaceOrDot();//读取停止的时候指针一定不会对着空白符或 .
        if (currentIdx >= len) yield break;

        //读取第二次
        curChar = quotedName[currentIdx];
        if (curChar == '"' || curChar == '`' || curChar == '[')
        {
            //有引用符
            currentIdx++;
            yield return readQuotedSeg(curChar == '[' ? ']' : curChar, curChar != '[');
        }
        else
        {
            //没有引用符
            yield return readUnQuotedSeg();
        }
    }
    /// <inheritdoc />
    public override TableName ParseQuotedName(string quotedName)
    {
        if (quotedName.IsNullOrWhiteSpace()) return null;
        //基础解析
        var segArr = AnalysisQuotedName(quotedName).ToArray();
        if (segArr.IsNullOrEmpty()) throw new Exception($"解析对象名称失败: {quotedName}");

        if (segArr.Length == 1)
        {
            var name = segArr[0];
            AssertUtil.NotNullOrWhiteSpace(name);
            return new TableName(EnumDBType.Sqlite, name);
        }
        else
        {
            //"otherdb".`table1` or  [table1]
            //sqlite 中 最多两级
            var length = segArr.Length;
            var schemaName = segArr[length - 2];
            var name = segArr[length - 1];
            AssertUtil.NotNullOrWhiteSpace(schemaName);
            AssertUtil.NotNullOrWhiteSpace(name);
            return new TableName(
                EnumDBType.Sqlite,
                name: name,
                schemaName: schemaName,
                dataBaseName: schemaName);
        }
    }
    /// <inheritdoc />
    public override TableName CreateTableName()
    {
        return new TableName(EnumDBType.Sqlite);
    }
    #endregion
}
